/**
* 
*  Processeur LEAT 
* see <a href="{@docRoot}/Documentation/processor.html"> for more information</a>
*  Copyright (c) 2009 Ecole Centrale de Nantes IRCCyN
* @author: Urunuela Richard
* @version
*/
package storm.Processors;


import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;

import log.DVSTrace;
import log.penalityTrace;
import storm.Schedulers.Scheduler;
import storm.Tasks.Task;

/**
 * 
 * @author richard
 *
 */
public class LEATProcessor extends Processor {
	
	double [][]profil_run={{0.925,624},{0.747,520},{0.570,416},{0.390,312},{0.279,208},{0.116,104}};
	double [][]profil_idle={{0.260,624},{0.222,520},{0.186,416},{0.154,312},{0.129,208},{0.064,104}};
	
	double []profil_other={0.0017,0.000163,0.000101};
	double [][]profil_penality={{0.34,11.43},{2.5,136.65},{3.6,261.75}};
	int penality=0;
	int compteur_running=0;
	public States State= States.IDLE;
	States predState= States.IDLE;
	int internal_dvfs_state= 0; 

	boolean transition = false;
	int compteur_idle=0;
	public void init(){
		super.init();
		int freq = 0;
		if (getOwnFieldStringValue("freq")!=null )
		getOwnFieldIntValue("freq");
		this.setFreq(freq);
		if (this.getOwnFieldStringValue("cache" ) !=null  ) {
			Class Classe;
			try {
				Classe = Class.forName(this.getOwnFieldStringValue("cache" ) );
			   Constructor N =  Classe.getConstructor();
	
			    Cache C = (Cache)N.newInstance();
			    C.setKernel (this.getKernel());
			    C.setOwnFieldStringValue("BurstModeAccesTime",this.getOwnFieldStringValue("BurstModeAccesTime"));
			    
			    if (this.getOwnFieldStringValue("Cache_Size" ) != null) {
			    	C.setOwnFieldStringValue("Cache_Size",this.getOwnFieldIntValue("Cache_Size")+"");
			    	
			    	if (this.getOwnFieldStringValue("cartofile" ) != null) C.setOwnFieldStringValue("cartofile",this.getOwnFieldStringValue("cartofile")+"");;
			    	if (this.getOwnFieldStringValue("logfile" ) != null) C.setOwnFieldStringValue("logfile",this.getOwnFieldStringValue("logfile")+"");;
			    	
			    	C.setMemory(this.getOwnFieldIntValue("Cache_Size"));
			    }
			    this.cache = C;
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		else {
		
		}
	}
	Cache cache = null;
	/**
	 * 
	 * @author richard
	 *
	 */
	public enum States {
		RUNNING ,
		IDLE ,
		STAND_BY ,
		SLEEP ,
		DEEP_SLEEP
	}
	/**
	 * 
	 * @return
	 */
	public int getCompteur_idle() {
		return compteur_idle;
	}
	/**
	 * 
	 * @param compteur_idle
	 */
	public void setCompteur_idle(int compteur_idle) {
		this.compteur_idle = compteur_idle;
	}
	/**
	 * 
	 * @return
	 */
	public int getCompteur_running() {
		return compteur_running;
	}
	/**
	 * 
	 * @param compteur_running
	 */
	public void setCompteur_running(int compteur_running) {
		this.compteur_running = compteur_running;
	}
	/**
	 * 
	 */
	public void setState(States st){
		/**
		 * transition sleep normale
		 */
		if (this.State == st )  return;
		 if (this.penality == 0 ) this.predState = st; 
		if (this.State == States.DEEP_SLEEP ){
			this.penality =  (int) Math.round((profil_penality[2][1]*this.Kernel.getprecision()))+ 	this.penality ;
			this.Kernel.getLogs().addTrace(new log.penalityTrace(this.Kernel.getTime(), penalityTrace.Type.PROCDVFS,"TRANSITION from DEEP SLEEP "+this.penality));
		}
		/**
		 * transition  normale sleep
		 */
		if (st == States.DEEP_SLEEP ){
			this.penality =  (int) Math.round((profil_penality[2][0]*this.Kernel.getprecision()))+ 	this.penality ;
			this.Kernel.getLogs().addTrace(new log.penalityTrace(this.Kernel.getTime(), penalityTrace.Type.PROCDVFS,"TRANSITION to DEEP SLEEP "+this.penality));
			
		}
		
		
		/**
		 * transition standby normale
		 */
		if (this.State == States.STAND_BY ){
			this.penality =  (int) Math.round((profil_penality[0][1]*this.Kernel.getprecision()))+ 	this.penality ;
			this.Kernel.getLogs().addTrace(new log.penalityTrace(this.Kernel.getTime(), penalityTrace.Type.PROCDVFS,"TRANSITION from stand by "+this.penality));
		}	
		/**
		 * transition  normale standby
		 */
		if (st == States.STAND_BY ){
			this.penality =  (int) Math.round((profil_penality[0][0]*this.Kernel.getprecision()))+ 	this.penality ;
			this.Kernel.getLogs().addTrace(new log.penalityTrace(this.Kernel.getTime(), penalityTrace.Type.PROCDVFS,"TRANSITION to stand by "+this.penality));
			
		}
	//	this.predState = st;
		this.State = st;
	
		transition = true;
	}
	/**
	 * 
	 * @return
	 */
	public States getStates(){
		return this.State;
	}
	/**
	 * 
	 */
	public LEATProcessor() {
		super();
	}
	/**
	 * 
	 */
	public void setRunning(Task T){
		super.setRunning(T);
		this.State = State.RUNNING;
		if (this.isRunning()){
			this.majTask();
		}
	}
	/**
	 * 
	 */
	public int getDVSFactor() {
		// TODO Auto-generated method stub
		return (int) (profil_run[0][1]/profil_run[this.internal_dvfs_state][1]);
	}
	/**
	 * 
	 */
	public double getPower(){
		// dans une transition 
		// if (this.predState != this.State) {
		if (this.penality > 0 ) {
			
			 if (this.predState == LEATProcessor.States.RUNNING ) {
			
				 return (profil_run[this.internal_dvfs_state][0] * 1 /100);
			 }
			 else if (this.predState==States.IDLE){
		
				 return (profil_idle[this.internal_dvfs_state][0]);
			 }
			 else {
				 return (profil_other[predState.ordinal()-2]);
			 }
		 }
	 if (this.State == LEATProcessor.States.RUNNING ) {
		
		 return (profil_run[this.internal_dvfs_state][0] * this.getrunning().powerDeviation /100);
	 }
	 else if (State==States.IDLE){
		
		 return (profil_idle[this.internal_dvfs_state][0]);
	 }
	 else {
		 return (profil_other[State.ordinal()-2]);
	 }

	}

	/**
	 * 
	 */
	public void onTick(){
		//System.out.println( this.predState + "  " + this.State);
		if ( (this.penality == 0 )) {
			if (transition == true ) transition = false;
			this.predState = this.State;
		}
		if (!this.isRunning()){
			if (this.State == States.RUNNING ){ 
				
				this.setState( States.IDLE);
			}
			compteur_idle ++;
			this.compteur_running=0;
		}
		if (this.isRunning()){
			
			this.setState(States.RUNNING);
			if (this.penality > 0) {
				this.getrunning().setPenality(1);
				
			}
			else  if (this.cache !=null){
				int pen = cache.havePenality(this.getrunning());
				this.getrunning().setPenality(pen);
				
			}
			
			this.majTask();
			compteur_idle=0;
			this.compteur_running++;	
		}
		if(this.penality>0) this.penality --;
		super.onTick();
	}
	/**
	 * 
	 */
	public int getFreq() {
		return this.internal_dvfs_state;
	}
	public double getFreq(float dvsfactorNew) {
		// TODO Auto-generated method stub
		return (this.profil_run[0][1]/dvsfactorNew);
	}
	/**
	 * 
	 */
	public String SondetoString(){
		int id =0;
		String name = " ";
		if (this.isRunning()) {
			name = this.getrunning().getName();
			id = this.getrunning().id;
		}
		String res = this.getName() + "## State:" + this.State + "## id Task:" + id + "## name Tak:" + name + "## idle:" + this.compteur_idle + "## running:"  + this.compteur_running +  "## Freq:" + this.internal_dvfs_state + "## penality:" + this.penality; 
		
		return res;
	}
	/**
	 * 
	 */
	private void majTask() {
		/**
		 * On change le ret en fonction de la fr??quence :)
		 */
		double freqMax = this.profil_run[0][1];
		double currentFreq = this.profil_run[this.internal_dvfs_state][1];
		Task T = this.getrunning();
		if (T!=null){
		float dvsfactor = (float) (freqMax/currentFreq);
		float r = T.getRET();
		if (T.getDvsFactor()!=dvsfactor) {
			
			r = r / T.getDvsFactor();
			r = r * dvsfactor;
			r = Math.round(r);
			/*
			 Log DVS 
			 */
			DVSTrace trace = new DVSTrace(this.Kernel.getTime(), T, this,T.getDvsFactor() ,dvsfactor, T.getRET(), r);
			this.Kernel.getLogs().addTrace(trace);
			T.setRet((int) r);
			T.setDvsFactor(dvsfactor);
		}
		}
	}
	/**
	 * 
	 * @param i
	 */
	public void setFreq(int i) {
		if ((i>=0 )&& (i<=5)){
			this.internal_dvfs_state = i;
			this.majTask();
		}
		// TODO Auto-generated method stub
	}
}
